package veetouch.syncpc;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.LuminanceSource;
import com.google.zxing.NotFoundException;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.encoder.QRCode;

import veetouch.network.ConnectionManager;
import veetouch.bitmap.PlanarYUVLuminanceSource;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.AlertDialog.Builder;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.net.Uri;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.Toast;

public class VtAndroidSyncActivity extends Activity implements SurfaceHolder.Callback , Camera.PreviewCallback 
{
	//////////////////////////////////////////////////////////
	//
	// Type of Sync connection to Server
	//
	//////////////////////////////////////////////////////////
	public static final int SYNC_TYPE_DOWNLOAD 			= 0;		// Download data from server
	public static final int SYNC_TYPE_UPLOAD_PHOTO  	= 1; 		// Upload photo to server 
	public static final int SYNC_TYPE_UPLOAD_LINK   	= 2; 		// Upload link to server
	public static final int SYNC_TYPE_UPLOAD_CONTACT   	= 3; 		// Upload contact to server
	
	//////////////////////////////////////////////////////////
	//
	// QR TAG CODE
	//
	//////////////////////////////////////////////////////////
	
	// Server connection pattern
	private static Pattern QR_SHARING_PATTERN 			= Pattern.compile("^([0-9]+[.][0-9]+[.][0-9]+[.][0-9]+):([0-9]+)$");
	private static Pattern QR_REQUEST_ITEM_PATTERN 		= Pattern.compile("^([0-9]+[.][0-9]+[.][0-9]+[.][0-9]+):([0-9]+):ID:([0-9]+)$");
	
	//Default QR Code pattern
	private static Pattern QR_HTTP_PATTERN 				= Pattern.compile("^(http://|https://)"); 						// Web site pattern
	private static Pattern QR_TEL_PATTERN 				= Pattern.compile("^(TEL|tel):(.*$)"); 						 	// Tel to number pattern
	private static Pattern QR_WIFI_PATTERN				= Pattern.compile("^WIFI:T:(WPA|WEP);S:(.*?);P:(.*?);;$"); 		// Wifi connection pattern
	private static Pattern VCARD_PATTERN				= Pattern.compile("^BEGIN:VCARD.*?N:(.*?)[\n\r].*END:VCARD$",Pattern.DOTALL); 	// vCARD pattern
	
	//////////////////////////////////////////////////////////
	//
	// DIALOG ID
	//
	//////////////////////////////////////////////////////////
	public static final int DIALOG_QR_TEXT 		= 0;	// Display qr text
	public static final int DIALOG_NOTICE 		= 1;	// Display notice dialog
	public static final int DIALOG_OPEN_BROWSER = 2;	// Display open browser
	public static final int DIALOG_TEL_TO 		= 3;	// Display tel to number
	public static final int DIALOG_CONNECT_WIFI = 4;	// Display connect to wifi confirm
	public static final int DIALOG_ADD_CONTACT  = 5;	// Display add contact confirm
	
	private String qrText  = "";
	private String errText = "";
	
	//////////////////////////////////////////////////////////
	
	// Bundle
	public Bundle extras;
	
	// Network
	private ConnectionManager connection;
	
	// Get data
	public AlertDialog dialog; 			// Popup dialog
	public ProgressDialog progressDialog;	// Progress dialog when connection is loading
	
	// Ui
	private SurfaceView surface;
	private Reader reader;
	private SurfaceHolder holder;
	private Camera mCamera;	
	private Size size;
	
	private int previewFormat;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.qrtrack);
        
        // Set default button
        Button button_home = (Button) findViewById(R.id.Button_home);
        button_home.setOnClickListener(new OnClickListener() 
		{
			public void onClick(View arg0) 
			{
				onBackPressed();
			}
		});
        
        this.connection = new ConnectionManager(this);
        
        this.reader = new QRCodeReader();
        
        mCamera = Camera.open();
        this.previewFormat = this.mCamera.getParameters().getPreviewFormat();
        // Only support ImageFormat.NV21 and ImageFormat.YUY2 for now
        if(this.previewFormat != ImageFormat.NV21 || this.previewFormat != ImageFormat.YUY2)
        {
        	this.previewFormat = ImageFormat.NV21;
        }
        Camera.Parameters parameters = this.mCamera.getParameters();
        
        Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
        int width  = display.getWidth();
        int height = display.getHeight();
        // Rotate camera display
        if(display.getRotation() == Surface.ROTATION_0)
        {
            parameters.setPreviewSize(height, width);                           
            mCamera.setDisplayOrientation(90);
        }
        else if(display.getRotation() == Surface.ROTATION_90)
        {
            parameters.setPreviewSize(width, height);                           
        }
        else if(display.getRotation() == Surface.ROTATION_180)
        {
            parameters.setPreviewSize(height, width);               
        }
        else if(display.getRotation() == Surface.ROTATION_270)
        {
            parameters.setPreviewSize(width, height);
            mCamera.setDisplayOrientation(180);
        }
        mCamera.setParameters(parameters);

		this.size = parameters.getPreviewSize();
        //this.mCamera.setPreviewCallback(this);
        this.surface = (SurfaceView) findViewById(R.id.surfaceViewCam);
        holder = surface.getHolder();
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        holder.setSizeFromLayout();
        holder.addCallback(this);

        // Set Bundle Sync type
        this.extras = getIntent().getExtras();
    } 
    
    /* (non-Javadoc)
	 * @see android.app.Activity#onCreateDialog(int)
	 */
	@Override
	protected Dialog onCreateDialog(int id) 
	{
		Builder builder;
		switch (id) {
		 	// Only text
			case VtAndroidSyncActivity.DIALOG_QR_TEXT:
				builder = new AlertDialog.Builder(this);
				builder.setMessage("QR Code text is "+this.qrText+".");
				builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() 
				{
	                public void onClick(DialogInterface dialog, int which) 
	                {
	                	mCamera.startPreview();
	                }
	            });
				dialog = builder.create();
	            dialog.show();
			break;
			// Error text
			case VtAndroidSyncActivity.DIALOG_NOTICE:
				builder = new AlertDialog.Builder(this);
				builder.setMessage(this.errText);
				builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() 
				{
	                public void onClick(DialogInterface dialog, int which) 
	                {
	                	mCamera.startPreview();
	                }
	            });
				dialog = builder.create();
	            dialog.show();
	            break;
	        // Web site
			case VtAndroidSyncActivity.DIALOG_OPEN_BROWSER:
				builder = new AlertDialog.Builder(this);
				builder.setMessage("Open web site:\n"+this.qrText+"");
				builder.setCancelable(true);
	            builder.setPositiveButton("Open", new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int which) {
	                	Intent i = new Intent(Intent.ACTION_VIEW);
	                	i.setData(Uri.parse(qrText));
	                	startActivity(i);
	                }
	            });
	            builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int which) {
	                	mCamera.startPreview();
	                }
	            });
	            dialog = builder.create();
	            dialog.show();
	            break;
			case VtAndroidSyncActivity.DIALOG_TEL_TO:
				builder = new AlertDialog.Builder(this);
				Matcher matcher = QR_TEL_PATTERN.matcher(qrText);
				matcher.find();
				builder.setMessage("Tel to: "+matcher.group(2));
				builder.setPositiveButton("Call", new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int which) 
	                {
	                	Intent callIntent = new Intent(Intent.ACTION_CALL);
	                	callIntent.setData(Uri.parse(qrText.toLowerCase()));
	                	startActivity(callIntent);
	                }
	            });
	            builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int which) {
	                	mCamera.startPreview();
	                }
	            });
	            dialog = builder.create();
	            dialog.show();
				break;
			case VtAndroidSyncActivity.DIALOG_CONNECT_WIFI:
				builder = new AlertDialog.Builder(this);
				matcher = QR_WIFI_PATTERN.matcher(qrText);
				matcher.find();
				builder.setMessage("Connect to WIFI: "+matcher.group(2));
				builder.setPositiveButton("Connect", new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int which) 
	                {
	                	Matcher matcher = QR_WIFI_PATTERN.matcher(qrText);
	                	matcher.find();
	                	WifiManager wifi = (WifiManager)getSystemService(Context.WIFI_SERVICE);
	                	if(!wifi.isWifiEnabled())
	                	{
	                		wifi.setWifiEnabled(true);
	                		try {Thread.sleep(1000);} catch (InterruptedException e) {}
	                	}
	                	WifiConfiguration wc = new WifiConfiguration();
	                	wc.SSID = "\""+matcher.group(2)+"\"";
	                	wc.hiddenSSID = true;
	                	wc.status = WifiConfiguration.Status.DISABLED;
	                	wc.priority = 40;
	                    wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
	                    wc.allowedProtocols.set(WifiConfiguration.Protocol.RSN); 
	                    wc.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
	                    wc.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
	                    wc.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
	                    wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
	                    wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
	                    wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
	                    wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
	                    wc.wepKeys[0] = "\""+matcher.group(3)+"\""; 	//This is the WEP Password
                		wc.wepTxKeyIndex = 0;
                	
                	    int res = wifi.addNetwork(wc);
                	    Log.d("WifiPreference", "add Network returned " + res );
                	    boolean es = wifi.saveConfiguration();
                	    Log.d("WifiPreference", "saveConfiguration returned " + es );
                	    boolean b = wifi.enableNetwork(res, true);   
                	    Log.d("WifiPreference", "enableNetwork returned " + b );
                	    
                	    mCamera.startPreview();
	                }
	            });
	            builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int which) {
	                	mCamera.startPreview();
	                }
	            });
	            dialog = builder.create();
	            dialog.show();
				break;
				
			case VtAndroidSyncActivity.DIALOG_ADD_CONTACT:
				builder = new AlertDialog.Builder(this);
				matcher = VCARD_PATTERN.matcher(qrText);
				matcher.find();
				builder.setMessage("Add contact: "+matcher.group(1));
				builder.setPositiveButton("Add", new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int which) 
	                {
	                	// Get data
	    				byte[] byeDataArray = qrText.getBytes();
	    				
	    				// Write vCard into temp file
	    				File fileDirectory = new File("/sdcard/vSync/");
	    				fileDirectory.mkdirs();
	    				final String filename = "vCard.vcf";
	    				File file = new File("/sdcard/vSync/",filename);
	    				if (file.exists())
	    				{
	    					file.delete();
	    				}
	    				try 
	    				{
	    					FileOutputStream fos = new FileOutputStream(file.getPath());
	    		        	fos.write(byeDataArray);
	    		        	fos.close();
	    				}
	    				catch (java.io.IOException e) 
	    				{
	    					mCamera.startPreview();
	    					return;
	    				}
	    				Intent saveContact = new Intent();
	    				saveContact.setAction(android.content.Intent.ACTION_VIEW);
	    				saveContact.setDataAndType(Uri.parse("file:///mnt/sdcard/vSync/vCard.vcf"), "text/x-vcard");
	    				startActivity(saveContact);
	    				mCamera.startPreview();
	                }
	            });
	            builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
	                public void onClick(DialogInterface dialog, int which) {
	                	mCamera.startPreview();
	                }
	            });
	            dialog = builder.create();
	            dialog.show();
				break;
		}
		return super.onCreateDialog(id);
	}

	public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) 
	{
		try 
		{
			if(this.mCamera != null)
			{
				this.mCamera.stopPreview();
				this.mCamera.setPreviewDisplay(holder);
				this.mCamera.setPreviewCallback(this);
				this.mCamera.startPreview();
			}
		}
		catch (IOException e) 
		{

		}
	}

	public void surfaceCreated(SurfaceHolder arg0) 
	{
		try 
		{
			if(this.mCamera != null)
			{
				this.mCamera.setPreviewDisplay(holder);
				this.mCamera.setPreviewCallback(this);
				this.mCamera.startPreview();
			}
		} 
		catch (IOException e)
		{
			
		}
    	
	}

	public void surfaceDestroyed(SurfaceHolder arg0) 
	{
		if (this.mCamera != null)
        {
			this.mCamera.setPreviewCallback(null);
        	this.mCamera.stopPreview();
        	this.mCamera.release();
        	this.mCamera = null;
        }
		this.finish();
	}
	
	public void onDestroy()
	{
		super.onDestroy();
	}

	public void onPreviewFrame(byte[] arg0, Camera arg1) 
	{	
		if(dialog !=null)
		{
			if(dialog.isShowing())
			{
				return;
			}
		}
		
		LuminanceSource source = new PlanarYUVLuminanceSource(arg0,size.width,size.height,0,0,size.width,size.height,false);
		BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
		try 
		{
			Result result = this.reader.decode(binaryBitmap);
			if(result == null)
			{
				return;
			}
			else if(result.getText() == null)
			{
				return;
			}
			
			// pause the camera
			this.mCamera.stopPreview();
			qrText = result.getText();
			parseResultText();
		} 
		catch (NotFoundException e) 
		{
			//debugT.setText("NotFoundException");
		} 
		catch (ChecksumException e) 
		{
			//debugT.setText("ChecksumException");
		} 
		catch (FormatException e) 
		{
			//debugT.setText("FormatException");
		}
	}
	
	private void parseResultText()
	{
		// 1. SYNC SERVER and CLIENT
		int type = this.extras.getInt("SYNC_TYPE");
		
		// 1.1 Share data to server
		Matcher matcher = QR_SHARING_PATTERN.matcher(qrText);
		if(matcher.find())
		{
			// Type mis match
			if(type == VtAndroidSyncActivity.SYNC_TYPE_UPLOAD_PHOTO)
			{
				String address = matcher.group(1);
				int port = Integer.parseInt(matcher.group(2));
				startSyncWithServer(address,port);
			}
			else if(type == VtAndroidSyncActivity.SYNC_TYPE_UPLOAD_LINK)
			{
				String address = matcher.group(1);
				int port = Integer.parseInt(matcher.group(2));
				startSyncWithServer(address,port);
			}
			else if(type == VtAndroidSyncActivity.SYNC_TYPE_UPLOAD_CONTACT)
			{
				String address = matcher.group(1);
				int port = Integer.parseInt(matcher.group(2));
				startSyncWithServer(address,port);
			}
			else
			{
				this.qrText = "";
				this.errText = "This QR Code use only for upload data to server.";
				showDialog(VtAndroidSyncActivity.DIALOG_NOTICE);
			}
			return;
		}
		
		// 1.2 Get data frome server
		matcher = QR_REQUEST_ITEM_PATTERN.matcher(qrText);
		if(matcher.find())
		{
			// Type mis match
			if(type != VtAndroidSyncActivity.SYNC_TYPE_DOWNLOAD)
			{
				this.qrText = "";
				this.errText = "This QR Code use only for download data from server.";
				showDialog(VtAndroidSyncActivity.DIALOG_NOTICE);
			}
			else
			{
				String address 	= matcher.group(1); //Server IP
				int port 		= Integer.parseInt(matcher.group(2));
				int itemId   	= Integer.parseInt(matcher.group(3));
				this.extras.putInt("ITEM_ID",itemId);
				startSyncWithServer(address,port);
			}
			return;
		}
		
		// 2.1 HTTP QR CODE TAG
		matcher = QR_HTTP_PATTERN.matcher(qrText);
		if(matcher.find())
		{
			showDialog(VtAndroidSyncActivity.DIALOG_OPEN_BROWSER);
			return;
		}
		
		// 2.2 TEL QR CODE TAG
		matcher = QR_TEL_PATTERN.matcher(qrText);
		if(matcher.find())
		{
			showDialog(VtAndroidSyncActivity.DIALOG_TEL_TO);
			return;
		}
		
		// 2.3 Connect to wifi
		matcher = QR_WIFI_PATTERN.matcher(qrText);
		if(matcher.find())
		{
			showDialog(VtAndroidSyncActivity.DIALOG_CONNECT_WIFI);
			return;
		}
		
		// 2.4 vCard scan
		matcher = VCARD_PATTERN.matcher(qrText);
		if(matcher.find())
		{
			showDialog(VtAndroidSyncActivity.DIALOG_ADD_CONTACT);
			return;
		}
		
		// 3. Normal text
		showDialog(VtAndroidSyncActivity.DIALOG_QR_TEXT);
	}
	
	private void startSyncWithServer(final String address,final int port)
	{
		Log.d("DEBUG","CALL startSyncWithServer");
		
		// 1. Open waiting dialog
		if(this.connection.runProcess == true)
		{
			return;
		}
		this.progressDialog = ProgressDialog.show(this,"","Sync...");
		if(this.connection.openConnection(address, port))
		{
			if(!this.connection.isAlive())
			{
				try
				{
					this.connection.start();
				}
				catch(Exception e)
				{
					
				}
			}
			//this.connection.startSyncRequest();
		}
		else
		{
			// Back to home
			this.onBackPressed();
			// Toast message network
			Toast.makeText(getApplicationContext(),"Please check your network connection.",Toast.LENGTH_LONG).show();
		}
	}
}